package com.wa.es.my;

import cn.hutool.core.lang.Console;
import org.apache.http.HttpHost;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.index.query.functionscore.ScriptScoreFunctionBuilder;
import org.elasticsearch.index.search.MatchQuery;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.*;

public class XqqMatchTest {

    static RestHighLevelClient highLevelClient;

    @Before
    public void getClient() throws Exception {
        // Low Level Client init
        highLevelClient = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.1.105", 9200, "http")));
        XqqMatchTest.highLevelClient = highLevelClient;
    }

    @After
    public void close() throws Exception {
        // Low Level Client init
        highLevelClient.close();
    }


    public static SearchResponse getSearchHits(String indexName, SearchSourceBuilder searchSourceBuilder){
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchSourceBuilder.trackTotalHits(Boolean.TRUE);

        //https://www.elastic.co/guide/cn/elasticsearch/guide/current/relevance-intro.html#explain
        //输出 explain 结果代价是十分昂贵的，它只能用作调试工具 。千万不要用于生产环境。
        //builder.setExplain(true);
        Console.log("index name:{}",indexName);
        Console.log("执行查询语句:{}",searchSourceBuilder.toString());
        searchRequest.source(searchSourceBuilder);

        return getResponseData(searchRequest);
    }

    /**
     * 执行查询
     * @param searchRequest
     * @return
     */
    public static SearchResponse getResponseData(SearchRequest searchRequest) {
        SearchResponse searchResponse = null;
        try {
            long startTime = System.currentTimeMillis();
            searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            long endTime = System.currentTimeMillis();
            Console.log("执行搜索查询时间:{}",(endTime-startTime));
        } catch (Exception e) {
            Console.error("执行查询异常{}",e);
            e.printStackTrace();
        }
        return searchResponse;
    }


    private String productIndex = "product_alias_test";

    /**
     *  (多个)型号精准搜索+(多个)品牌精准搜索产品库
     */
    @Test
    public void productMatchFullPartNumber(){
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //返回的文档必须满足filter子句的条件,但是不会像must一样,参与计算分值
        List<QueryBuilder> filterList = boolQuery.filter();
        List<String> partNumberKeywordList = new LinkedList<>();
        partNumberKeywordList.add("GCM32EC71H106MA03L");
        partNumberKeywordList.add("MCN12FD330J");
        TermsQueryBuilder partNumberTermQueryBuilder = QueryBuilders.termsQuery("partNumber.rawPartNumber", partNumberKeywordList);
        filterList.add(partNumberTermQueryBuilder);
        List<String> brands = new LinkedList<>();
        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("standardBrandId",brands);
        //包裹查询只返回一个常数分数等于提高每个文档的查询
        termsQueryBuilder.boost(10.0f);
        boolQuery.should(termsQueryBuilder);
        //一个封装了另一个查询的查询，它只是为查询中的每个文档返回一个等于查询boost的常量分数
        ConstantScoreQueryBuilder constantScoreQueryBuilder = QueryBuilders.constantScoreQuery(boolQuery);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(constantScoreQueryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     * 产品型号匹配
     */
    @Test
    public void productPartNumBuilder() {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        List<QueryBuilder> filterList = boolQuery.filter();
        TermsQueryBuilder partNumberTermQueryBuilder = QueryBuilders.termsQuery("partNumber.rawPartNumber", "123");
        filterList.add(partNumberTermQueryBuilder);
        //优先级
        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("standardBrandId", "123", "456", "65");
        boolQuery.should(termsQueryBuilder);
//        ConstantScoreQueryBuilder constantScoreQueryBuilder = QueryBuilders.constantScoreQuery(boolQuery);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQuery);
        SearchResponse searchResponse = QueryTest.getSearchHits(productIndex, searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     * 产品参数匹配
     */
    @Test
    public void productParamBuilder() {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //商品分类
        MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("categoryNameSeparate", "连接器 连接器 圆形连接器");   //顺序相同
        matchPhraseQueryBuilder.analyzer("whitespace");
        matchPhraseQueryBuilder.zeroTermsQuery(MatchQuery.ZeroTermsQuery.NONE);
        boolQueryBuilder.must(matchPhraseQueryBuilder);
        //必要参数
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("paramDesc", "银 22A 自由悬挂 防水 IP67 防尘");
        matchQueryBuilder.analyzer("product_desc_analyzer");
        matchQueryBuilder.autoGenerateSynonymsPhraseQuery(false);
        matchQueryBuilder.minimumShouldMatch("1%");
        matchQueryBuilder.operator(Operator.AND);
        boolQueryBuilder.must(matchQueryBuilder);


        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQueryBuilder);
        SearchResponse searchResponse = QueryTest.getSearchHits(productIndex, searchSourceBuilder);
        Console.log(searchResponse);
    }


    @Test
    public void functionScoreQueryBuilder() {
        List<FunctionScoreQueryBuilder.FilterFunctionBuilder> list = new ArrayList<>();
        Map<String, Object> params = new HashMap<>();
        params.put("quantity", 100);
        String SCRIPT_FOR_LADDER_PRICE = "if(null == params._source.priceList) return 0;long sum = 0; ArrayList list = params._source.priceList; int j=list.length;for(int i= 0; i < j; i++ ){if(params.quantity >= list[j-i-1].minAmount){sum = list[j-i-1].price; break;}} if(0 == sum){sum = list[0].price * 10000l} return 1/Math.log10(sum + 1);";
        Script script = new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, SCRIPT_FOR_LADDER_PRICE, params);
        ScriptScoreFunctionBuilder scriptQueryBuilder = ScoreFunctionBuilders.scriptFunction(script);
        FunctionScoreQueryBuilder.FilterFunctionBuilder filterFunctionBuilder = new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptQueryBuilder);
        list.add(filterFunctionBuilder);


        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //必要参数
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("paramDesc", "Cap Ceramic 27pF 50V C0G 5% Pad SMD 0603 125°C Extreme Low ESR Automotive T/R");
        matchQueryBuilder.analyzer("ware_desc_analyzer");
        matchQueryBuilder.autoGenerateSynonymsPhraseQuery(false);
        matchQueryBuilder.minimumShouldMatch("50%");
        matchQueryBuilder.operator(Operator.OR);
        boolQueryBuilder.must(matchQueryBuilder);


        QueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(boolQueryBuilder, list.toArray(new FunctionScoreQueryBuilder.FilterFunctionBuilder[list.size()]))
                .boostMode(CombineFunction.SUM)
                .scoreMode(FunctionScoreQuery.ScoreMode.SUM);


        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(functionScoreQueryBuilder);
        SearchResponse searchResponse = QueryTest.getSearchHits("test_partnum_entity_alias", searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     *  分词精确查询 一次匹配一个值
     */
    @Test
    public void productNestedQuery(){
        //前面带1匹配不上
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //可以套一层嵌套
        BoolQueryBuilder xx = QueryBuilders.boolQuery();
        NestedQueryBuilder nestedQuery = new NestedQueryBuilder("pictureObjList", new TermQueryBuilder("pictureObjList.fileUrl","https://www.mouser.cn/images/molex/images/33471-02_iso.jpg"), ScoreMode.None);   //注意：除path之外，fieldName也要带上path (partNoAliasList)
        xx.should(nestedQuery);
        boolQueryBuilder.must(xx);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
//                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQueryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }
}
